#include "ScratchWindowsFramework.h"

#ifdef _WIN32
#ifdef _DEBUG
   #include <crtdbg.h>
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
   #define new       new( _NORMAL_BLOCK, __FILE__, __LINE__)
   #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#endif

#pragma warning(disable:4355)

using namespace acme::scratch;
using namespace acme::windows;

///////////////////////////////////////////////////////////////////////////////

int STApplication::Main(ApplicationDelegate* delegate)
{
  CoInitialize(NULL);
  MSG msg;

  delegate->applicationDidFinishLaunching();

  while(GetMessage(&msg,0,0,0)>0)
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  delegate->applicationWillTerminate();

  CoUninitialize();
  return msg.wParam;
}

///////////////////////////////////////////////////////////////////////////////

Window::Window()
 : _hwnd(0),_delegate(0)
{
}

Window::~Window()
{
  if(_hwnd)
  {
    SetWindowLongPtr(_hwnd,GWLP_USERDATA,0);
    DestroyWindow(_hwnd);
  }
}

void Window::initWithContentRect(Rect frame)
{
  int x = frame.left;
  int y = frame.top;
  int w = frame.right - frame.left;
  int h = frame.bottom - frame.top;

  Create(x,y,w,h,TEXT("ACME"));
}

void Window::makeKeyAndOrderFront()
{
  // make sure its visible
  // make sure its active
  // make sure its active
  ShowWindow(_hwnd,SW_SHOWNORMAL);
}

void Window::performClose()
{
  SendMessage(_hwnd,WM_CLOSE,0,0);
}


void Window::setContentView(View* content)
{
  _content = content;
  // repaint
}

void Window::setDelegate(WindowDelegate* delegate)
{
  _delegate = delegate;
}

LRESULT Window::WindowProcThunk(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  Window* self;
  if(uMsg == WM_CREATE)
  {
    LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
    self = (Window*)pcs->lpCreateParams;
    self->_hwnd = hwnd;
    SetWindowLongPtr(hwnd,GWLP_USERDATA,(LONG_PTR)self);
  }
  else
    self = (Window*)GetWindowLongPtr(hwnd,GWLP_USERDATA);

  if(!self)
    return DefWindowProc(hwnd,uMsg,wParam,lParam);

  LRESULT ret = self->WindowProc(uMsg,wParam,lParam);

  if(uMsg == WM_DESTROY)
  {
    // 
    self = (Window*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
    self->_hwnd=0;
  }
  return ret;
}


LRESULT Window::WindowProc(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
  switch(uMsg)
  {
  case WM_ERASEBKGND:
    if(_content)
      return TRUE;
    break;
  case WM_SIZE:
    if(_delegate)
    {
      int width = LOWORD(lParam);
      int height = HIWORD(lParam);
      switch(wParam)
      {
      case SIZE_RESTORED:
      case SIZE_MAXSHOW:
      case SIZE_MAXIMIZED:
      case SIZE_MAXHIDE:
        _delegate->windowDidResize(width,height);
        break;
      case SIZE_MINIMIZED:
        //_delegate->windowDidIcon();
        break;
      }
    }
    break;
  case WM_MOVE:
    if(_delegate)
      _delegate->windowDidMove();
    break;
  case WM_PAINT:
    if(_delegate)
    {
      _delegate->windowDidUpdate();
      //return TRUE;
    }
    break;
  case WM_CLOSE:
    if(_delegate)
    {
      if(!_delegate->windowShouldClose())
        return TRUE;
    }
    break;
  case WM_TIMER:
    if(_delegate)
    {
      _delegate->windowTimer(wParam);
    }
    return TRUE;
  case WM_DESTROY:
    if(_delegate)
      _delegate->windowDidClose();
    return TRUE;
  }

  return DefWindowProc(_hwnd,uMsg,wParam,lParam);
}

void Window::Create(int x, int y, int w, int h,LPCTSTR windowTitle)
{
  if(_hwnd)
    throw Error(1);

  LPCTSTR className = TEXT("AcmeWindow");
  // TODO: use the current(dll) handle NOT the app!!!
  HMODULE module = GetModuleHandle(NULL); 

  DWORD exStyle = 0;
  DWORD style = WS_OVERLAPPEDWINDOW;

  HWND parentWnd = 0;
  HMENU menu = 0;

  WNDCLASSEX existClassInfo = {0};
  existClassInfo.cbSize = sizeof existClassInfo;

  WNDCLASSEX newClassInfo = {0};
  newClassInfo.cbSize = sizeof newClassInfo;
  newClassInfo.hbrBackground = (HBRUSH)COLOR_WINDOW;
  newClassInfo.hCursor = LoadCursor(NULL,IDC_ARROW);
  newClassInfo.hIcon = LoadIcon(NULL,IDI_APPLICATION);
  newClassInfo.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
  newClassInfo.hInstance = module;
  newClassInfo.lpfnWndProc = WindowProcThunk;
  newClassInfo.lpszClassName = className;
  newClassInfo.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;

  ATOM classAtom=0;
  BOOL exists = GetClassInfoEx(module,className,&existClassInfo);
  classAtom = RegisterClassEx(&newClassInfo);
  if(!classAtom)
    throw Error();

  HWND hwnd = CreateWindowEx(exStyle,(LPCTSTR)classAtom,windowTitle,style,x,y,w,h,parentWnd,menu,module,this);
}

///////////////////////////////////////////////////////////////////////////////


void Trace::Write(LPCTSTR string)
{
  static DWORD timestamp=GetTickCount();
  TCHAR buffer[1025];
  DWORD newTime=GetTickCount();
  DWORD delta = newTime-timestamp;
  int s=delta/1000;
  int ms=delta%1000;
  wsprintf(buffer,TEXT("%6d.%04d - %s\r\n"),s,ms,string);
//  timestamp = newTime;
  OutputDebugString(buffer);
}


void Trace::Warn(LPCTSTR string)
{
  Trace::Write(string);
  DebugBreak();
}

void Trace::WarnFormat(LPCTSTR string,...)
{
  va_list arglist;
  va_start(arglist,string);
  TCHAR buffer[1025];
  wvsprintf(buffer,string,arglist);
  va_end(arglist);
  Trace::Warn(buffer);
}

void Trace::WriteFormat(LPCTSTR string,...)
{
  va_list arglist;
  va_start(arglist,string);
  TCHAR buffer[1025];
  wvsprintf(buffer,string,arglist);
  va_end(arglist);
  Trace::Write(buffer);
}
